/*
 *  SymbolTable.cpp
 *  Compiler
 *
 *  Created by Jacopo De Stefani on 3/5/12.
 *  Copyright 2012 Politecnico di Milano. All rights reserved.
 *
 */

#include "ExtendedSymbolTable.h"

using namespace std;

ExtendedSymbolTable* ExtendedSymbolTable::uniqueInstance = NULL;

ExtendedSymbolTable* ExtendedSymbolTable::getInstance(){
	if (uniqueInstance == NULL) {
		uniqueInstance = new ExtendedSymbolTable();
	}
	return uniqueInstance;
}


//Adds a new entry in the table with default values and identifier id.
void ExtendedSymbolTable::addSymbolTableEntry(string identifier){
        pair<SymbolTable::iterator,bool> retValue;
        SymbolTableAttributes *attr = new SymbolTableAttributes();     
        retValue = symbolTable.insert(SymbolTableEntry(identifier,attr));
        if(!retValue.second){
            Writer::getInstance()->errorReport(DUPID,identifier);
            dupId = identifier;
        }
}

//Adds a new entry in the table with default values and identifier id.
void ExtendedSymbolTable::addFunctionTableEntry(string identifier){
	pair<FunctionTable::iterator,bool> retValue;
        queue<TypesP> *paramList = new queue<TypesP>;
	retValue = functionTable.insert(FunctionTableEntry(identifier,paramList));
        if(!retValue.second){
            
        }
}

//Updates the attributes of a list of variables using currVarList.
int ExtendedSymbolTable::updateSymbolTable(TypesP type,ScopesP scope,bool routineFlag,bool isArg){
	SymbolTable::iterator currEl,dupEl;
        string currId;
        int numEl = 0;
              
        if(!dupId.empty()){
            dupEl = symbolTable.find(dupId);
            
            if((dupEl->second)->getAddress() == -1){
                Writer::getInstance()->errorReport(DUPPROGID,dupEl->first);
                return -1;
            }
            if((dupEl->second)->getRoutineFlag() == true){
                if((dupEl->second)->getTypeP() == PROCT){
                        Writer::getInstance()->errorReport(DUPPROCID,dupEl->first);
                        return -1;
                }
                else{
                        Writer::getInstance()->errorReport(DUPFUNCID,dupEl->first);
                        return -1;
                }
            }
            else{
                 Writer::getInstance()->errorReport(HIDID,dupEl->first);
                 return -1;
            }
            dupId.clear();
        }
        
             
        
	while (!currVarList.empty()) {
                currId = popVar();
                
                currEl = symbolTable.find(currId);
                (currEl->second)->setTypeP(type);
                if(!isArg){
                        type == INTT ? Writer::getInstance()->putIntTop(0) : Writer::getInstance()->putBoolTop(false);
                }       
                (currEl->second)->setScopeP(scope);    
                scope == GLOBALS ?  (currEl->second)->setAddress(globctr++) : (currEl->second)->setAddress(locctr++); 
                (currEl->second)->setRoutineFlag(routineFlag);
                (currEl->second)->setScopeId(currScopeId);
                numEl++;
	}
        
             
        
        return numEl;
}

//Updates the parameters' list for a given function using currTypeList;
void ExtendedSymbolTable::updateTypeList(string identifier){
	FunctionTable::iterator currEl;
	currEl = functionTable.find(identifier);
        
        while(!(currTypeList.empty())){
        (currEl->second)->push(popType());    
        }
   
        flushTypeList();
	
}

//Check the correctness of actual parameters (type, order, number);
bool ExtendedSymbolTable::checkCallParameters(string identifier){
    FunctionTable::iterator currEl;
       
    if(!exists(identifier)){
        Writer::getInstance()->errorReport(UNDEFID,identifier);
        return false;
    }
    
    currEl = functionTable.find(identifier);
    queue<TypesP> originalTypeList,checkTypeList;
    
    
    while(!((currEl->second)->empty())){
        originalTypeList.push((currEl->second)->front());
        checkTypeList.push((currEl->second)->front());
        (currEl->second)->pop();
    }
    
        
    if(checkTypeList.size() != currTypeList.size()){
        Writer::getInstance()->errorReport(WRGPARNUM,identifier);
        return false;
    }
    
    while(!currTypeList.empty()){
        if(popType() != checkTypeList.front()){
            Writer::getInstance()->errorReport(PARTYPEMIS,identifier);
            return false;
        }
        checkTypeList.pop();
        
    }
    
    while(!originalTypeList.empty()){
        (currEl->second)->push(originalTypeList.front());
        originalTypeList.pop();
    }
    return true;
}

bool ExtendedSymbolTable::checkScope(string identifier){
    SymbolTable::iterator currEl;
    string elScope;
        
    if(!exists(identifier)){
        Writer::getInstance()->errorReport(UNDEFID,identifier);
        return false;
    }
    
    currEl = symbolTable.find(identifier);
    elScope = (currEl->second)->getScopeId();
    return elScope != currScopeId ? ((elScope == MAINSCOPE && currScopeId != MAINSCOPE) ? true : false ): true;
    
}
        
//Modifiers of the list of variables
void ExtendedSymbolTable::pushVar(string varId){
     currVarList.push_back(varId); 
}
string ExtendedSymbolTable::popVar(void){
	string retVal = currVarList.front();
	currVarList.pop_front();
        return retVal;
}

void ExtendedSymbolTable::flushVarList(){
	while (!currVarList.empty()) {
		currVarList.pop_front();
	}
}


void ExtendedSymbolTable::concVarList(queue<string> first,queue<string> second){
	while (second.size() != 0) {
		first.push(second.front());
		second.pop();
	}
}



//Modifiers of the list of parameters
void ExtendedSymbolTable::pushType(TypesP paramType){
     currTypeList.push(paramType);
}

TypesP ExtendedSymbolTable::popType(void){
	TypesP retVal = currTypeList.front();
	currTypeList.pop(); 
	return retVal;
}

void ExtendedSymbolTable::flushTypeList(){
	while (!currTypeList.empty()) {
		currTypeList.pop();
	}
}
						 
void ExtendedSymbolTable::concTypeList(queue<TypesP> first,queue<TypesP> second){
	while (second.size() != 0) {
		first.push(second.front());
		second.pop();
	}
}


//Gets attributes for a given entry in the symbol table
SymbolTableAttributes ExtendedSymbolTable::getSymbolTableAttributes(string identifier){
        return 	*((symbolTable.find(identifier))->second);
}

//Gets attributes for a given entry in the function table
queue<TypesP> ExtendedSymbolTable::getFunctionParameters(string identifier){
        return 	*functionTable.find(identifier)->second;
}

//Assigns a global address to the allocated identifier
void ExtendedSymbolTable::globalAddress(string identifier){
    SymbolTable::iterator currEl;
    
    currEl = symbolTable.find(identifier);
    (currEl->second)->setAddress(globctr++);
}

//Assigns a local address to the allocated identifier
void ExtendedSymbolTable::localAddress(string identifier){
    SymbolTable::iterator currEl;
    
    currEl = symbolTable.find(identifier);
    (currEl->second)->setAddress(locctr++);
}

//Sets to zero the counter for local addressing 
void ExtendedSymbolTable::restartLocalAddress(){
    locctr=0;
}

bool ExtendedSymbolTable::exists(string identifier){
     return symbolTable.find(identifier) == symbolTable.end() ? false : true;
}
